#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import re
import os.path
import fnmatch
import email
import quopri
#import urllib
import urlparse
import HTMLParser
from BeautifulSoup import BeautifulSoup
import inspect
import pickle
import optparse
import cmd
import subprocess
import codecs
import tempfile

def dbg_out(obj) :
    print "%s:%d" % inspect.stack()[1][1:3], obj

def is_host(addr) :
    if addr.endswith('/32') :
        return True
    else :
        return False

def default_lifetime(protocol) :
    if protocol == 'TCP' :
        return 1800
    elif protocol == 'UDP' :
        return 60
    elif protocol == 'ICMP' :
        return 10
    else :
        return 30

def escape_space(string) :
    return string.replace(" ", "|").encode('utf-8')

def escape_special_for_name(string) :
    t = string.replace(' ', '_')
    t = t.replace(',', '_')
    return t

N_HASH_MARK = 100 / 50
def update_progress(header, total, current) :
    last_progress = current * 100 / total
    progress = (current + 1) * 100 / total
    if last_progress != progress :
        sys.stdout.write('\r{0} [{1}] {2}% {3}/{4}'.format
                         (header, '#'*(progress/N_HASH_MARK), progress, current, total))
        sys.stdout.flush()

class Config :
    def __init__(self) :
        self.service_objects = []
        self.route_table = None
        self.network_interfaces = None
        self.network_objects = None
        self.rules = None
        self.nat_rules = None
        self.out_file = None
        self.split_object_translate_table = {} # key: original name, value: new name

    def register_mf2_config(self, mf2_config) :
        self.mf2_config = mf2_config

    def write(self, string) :
        if self.out_file == None :
            print >> sys.stderr, "Cannot write before set output file"
            return
        self.out_file.write(('%s\n' % string).encode('utf-8'))

    def set_output_file(self, filename) :
        if self.out_file != None :
            self.out_file.close()
        self.out_file = file(filename, 'w')

    def close_output_file(self) :
        if self.out_file != None :
            self.out_file.close()
        self.out_file = None

    def verify_rule(self) :
        for rule in self.rules :
            for src_obj in rule.source :
                found = False
                for net_obj in self.network_objects :
                    if net_obj.name == src_obj :
                        found = True
                        break
                if found == False :
                    print ("Unknown object for source : %s in Rule %s" 
                           % (src_obj, rule.sequence))
            for dst_obj in rule.destination :
                found = False
                for net_obj in self.network_objects :
                    if net_obj.name == dst_obj :
                        found = True
                        break
                if found == False :
                    print ("Unknown object for destination : %s in Rule %s" 
                           % (dst_obj, rule.sequence))
            for svc_obj_in_rule in rule.service :
                found = False
                for svc_obj in self.service_objects :
                    if svc_obj.name == svc_obj_in_rule :
                        found = True
                        break
                if found == False :
                    print ("Unknown service object : %s in Rule %s"
                           % (svc_obj_in_rule, rule.sequence))

    def lookup_zone_for_addr(self, addr) :
        "설정에서 주어진 주소의 zone을 찾는 함수"
        connected_interface = None
        # 라우팅 테이블 검색
        for route_entry in self.route_table[1:] :
            if route_entry.is_matched(addr) :
                connected_interface = route_entry.interface
                break

        #print "connected_interface = ", connected_interface
        if connected_interface == None :
            print "Failed to find the matched route entry"
            return None

        # 인터페이스 zone 검색
        for interface_entry in self.network_interfaces[1:] :
            #print "interface_entry.name = ", interface_entry.name
            if connected_interface == interface_entry.name :
                #print "interface_entry.zone = ", interface_entry.zone
                return interface_entry.zone
        return None

    def search_network_object_with_address(self, target_addr) :
        for net_obj in self.network_objects :
            for addr in net_obj.addr :
                if target_addr == addr :
                    return net_obj
        return None

    def process_host_object(self, name, zone, addr, description) :
        if len(addr) == 1 :
            host_addr = addr[0][:-3]
            real_zone = self.lookup_zone_for_addr(host_addr) 
            if real_zone != zone :
                print "WARNING: Adjust host(%s)'s zone from %s to %s" % (host_addr, zone, real_zone)
            host = MF2_Host_Object(name, real_zone, host_addr, description)
            self.mf2_config.add_host(host)
        else:
            print "ERROR: Host Object %s has more than 1 IP address." % name
            sys.exit(1)

    # process_group_object도 동작은 거의 유사하므로 이쪽으로 흠수 / 통합
    # 어울림 정책에서 그룹 객체의 경우, Zone이 의미를 갖지 않기 때문에 
    # 대충 설정하는 경향이 있다. 멤버들의 zone을 읽어서 그룹 객체의 zone을
    # 보정해야 한다.
    # 멤버들의 zone이 일치할 경우 다행이지만, 일치하지 않을 경우는 어떻게
    # 해야 할까? 경고를 주고 배제??
    def process_network_object(self, name, zone, addrs, description) :
        '''
        
        '''
        # 네트워크 그룹을 처리

        member_group = { 'internal': [], 'external': [], 'dmz': [], 'unknown': [] }

        for entry in addrs :
            if is_host(entry) :
                entry = entry[:-3]
                member_zone = self.lookup_zone_for_addr(entry)

                # 먼저 해당 zone에 그런 주소를 가진 호스트가 있는지 검사.
                found = self.mf2_config.lookup_host_object_with_addr_zone(entry, member_zone)
                if found != None :
                    member_group[member_zone].append("%sH %s" % (found.zone[0].upper(), found.name))
                else :
                    host_name = "%s_%s" %(entry, member_zone[0].upper())
                    # 그럼 다른 zone에는 있나?
                    duplicate = self.mf2_config.lookup_host_object_with_addr(entry)
                    if duplicate != None :
                        print "IGNORE: Object exist in different zone"
                        if duplicate.name == host_name :
                            print "WARNING: Object name duplicated"
                        
                    # 객체를 적당한 이름으로 생성
                    member_description = "Appended as member of %s" % name
                    host = MF2_Host_Object(host_name, member_zone, entry, member_description)
                    self.mf2_config.add_host(host)
                    member_group[member_zone].append("%sH %s" % (member_zone[0].upper(), host_name))

            elif entry.endswith('/0') :
                # 이런 애는 무시
                print "Error: %s was found out AA ANY in member of %s" % (entry, name)
                print "Skip this entry"
                #member.append("AA ANY")
            else:
                member_zone = self.lookup_zone_for_addr(entry)
                # if member_zone == None :
                #     for route_entry in self.route_table[1:] :
                #         print "ADDR = ", entry
                #         if route_entry.is_matched(entry) :
                #             connected_interface = route_entry.interface
                #             break

                found = self.mf2_config.lookup_network_object_with_addr_zone(entry, member_zone)
                if found != None :
                    member_group[member_zone].append("%sN %s" % (found.zone[0].upper(), found.name))
                else:
                    duplicate = self.mf2_config.lookup_network_object_with_addr(entry)
                    if duplicate != None :
                        print "IGNORE: Object exist in different zone"

                    if member_zone == None :
                        print "Cannot find zone for entry = %s(parent = %s)" % (entry, name)
                    else:
                        network_name = "%s_%s" % (entry, member_zone[0].upper())

                        member_description = "Appended as member of %s" % name
                        network = MF2_Network_Object(network_name, member_zone,
                                                 entry, member_description)
                        self.mf2_config.add_network(network)
                        member_group[member_zone].append("%sN %s" % (member_zone[0].upper(), network_name))

        #print "Member Group = ", member_group 
        non_zero_count = 0
        dominant_zone = zone
        dominant_zone_count = len(member_group[zone])
        for member_zone in member_group :
            zone_count = len(member_group[member_zone])
            #print member_zone, zone_count
            if zone_count != 0 :
                non_zero_count += 1
            if dominant_zone_count < zone_count :
                dominant_zone = member_zone
                dominant_zone_count = zone_count

        #print "non_zero_count = ", non_zero_count
        #print 'dominant_zone = ', dominant_zone
        #print 'dominant_zone_count = ', dominant_zone_count

        if non_zero_count == 0 :
            print 'ERROR: No members have valid zone in %s.' % name
            return
        
        if non_zero_count != 1 :
            print 'WARNING: Mixed Zone Group: split into %s groups' % non_zero_count
            #print '         Decription : %s' % description
            for member_zone in member_group :
                if len(member_group[member_zone]) != 0 :
                    new_name = '%s_%sG' % (name, member_zone[0].upper())
                    print '         Create new group %s' % new_name
                    if description.isspace() :
                        new_description = '%s (%s)' % (name, member_zone)
                    else :
                        new_description = '%s (%s)' % (description, member_zone)
                    #print '         Descripton : %s' % new_description
                    group = MF2_Group_Object(new_name, member_zone, member_group[member_zone], new_description)
                    self.mf2_config.add_group(group)
                    if not self.split_object_translate_table.has_key(name) :
                        self.split_object_translate_table[name] = []
                    self.split_object_translate_table[name].append(new_name)
            #print 'Only dominant zone(%s) members will be use (%s)' % (dominant_zone,
            #                                                           name)
        else :
            if dominant_zone != zone :
                print "WARNING: Adjust network(%s)'s zone from %s to %s" % (name, zone, dominant_zone)
                zone = dominant_zone

            group = MF2_Group_Object(name, zone, member_group[zone], description)
            self.mf2_config.add_group(group)

    def translate_network_object(self) :
        oulim_special_objects = {
            'IF-ALL' : '시스템 전체 인터페이스 주소',
            'NET-ALL' : '전체 인터넷',
            'IF-INT' : '시스템 내부 인터페이스 주소',
            'IF-EXT' : '시스템 외부 인터페이스 주소',
            'IF-SVC' : '시스템 서비스존 인터페이스 주소',
            'HOST-LOCAL' : '시스템 로컬 루프백(loopback) 주소',
            'IF-eth' : '시스템 로컬 네트워크 인터페이스',
            }
        # Only for Host 
        for net_obj in self.network_objects[1:] :
            if net_obj.type == 'Host' :
                self.process_host_object(net_obj.name, net_obj.zone, 
                                         net_obj.addr,
                                         net_obj.description)
            elif net_obj.type == 'Network' or net_obj.type == 'Host Group' :
                pass
            else :
                if (net_obj.name not in oulim_special_objects 
                    and net_obj.name[:6] not in oulim_special_objects) :
                    print("Unknown Object Type : %s in %s" 
                          % (net_obj.type, net_obj.name))
        # Then network and host group
        for net_obj in self.network_objects[1:] :
            if net_obj.type == 'Host' :
                pass
            elif net_obj.type == 'Network' :
                self.process_network_object(net_obj.name, net_obj.zone, 
                                            net_obj.addr, net_obj.description) 
            elif net_obj.type == 'Host Group' :
                self.process_network_object(net_obj.name, net_obj.zone,
                                            net_obj.addr, net_obj.description)
            else :
                if (net_obj.name not in oulim_special_objects 
                    and net_obj.name[:6] not in oulim_special_objects) :
                    print("Unknown Object Type : %s in %s" 
                          % (net_obj.type, net_obj.name))
            
                    #sys.exit(1)

    def classify_group_from_service(self) :
        svc_obj_list = []
        svc_grp_obj_list = []
        return svc_obj_list, svc_grp_obj_list

    def guess_service_type(self, service_object_name) :
        """ 서비스 객체의 이름에서 서비스 객체의 type을 추정하기 위한 함수임.
            HTTP로 시작하되 HTTPS로 시작하지 않으면 HTTP type.
            FTP, SMTP, POP3, SQLNET으로 시작하면 각각 해당 이름의 type.
            H323으로 시작할 경우, H.323 type. """
        # print "service_object_name = %s" % service_object_name
        if (re.match("^HTTP", service_object_name, re.I) and 
            not re.match("^HTTPS", service_object_name, re.I)) :
            print "Matched %s to HTTP" % service_object_name
            return "HTTP"
        if re.match("^FTP", service_object_name, re.I) :
            print "Matched %s to FTP" % service_object_name
            return "FTP"
        if re.match("^SMTP", service_object_name, re.I) :
            print "Matched %s to SMTP" % service_object_name
            return "SMTP"
        if re.match("^POP3", service_object_name, re.I) :
            print "Matched %s to POP3" % service_object_name
            return "POP3"
        if re.match("^SQLNET", service_object_name, re.I) :
            print "Matched %s to SQLNET" % service_object_name
            return "SQLNET"
        if (re.match("^H323", service_object_name, re.I) or 
            re.match("^H.323", service_object_name, re.I)):
            print "Matched %s to H.323" % service_object_name
            return "H.323"
        return "NONE"

    def translate_service_object(self) :
        # temporary list
        service_object_list, service_group_object_list = self.classify_group_from_service()
        
        KNOWN_PROTOCOL = set([ "AH", "EIGRP", "ESP", "GRE", "IGMP", "IGP", "IPComp", "IPv4",
                               "ISIS", "L2TP", "OSPF", "PIM", "RSVP", "SKIP" ])

        # 서비스 오브젝트를 만들 것인지 서비스 그룹 오브젝트를 만들 것인지 결정
        for svc_obj in self.service_objects :
            object_type = 'homogenious'
            for seq, entry in enumerate(svc_obj.port) :
                protocol_port_list = entry.split('/')
                if len(protocol_port_list) == 1 :
                    print "Ignore Service Entry: %s" % entry
                    object_type = 'special'
                elif len(protocol_port_list) == 2 :
                    print "Register Protocol Entry: %s" % entry
                    if protocol_port_list[0] not in KNOWN_PROTOCOL :
                        print "Unknown protocol : %s" % protocol_port_list[0]
                    else :
                        if len(svc_obj.port) != 1 :
                            object_type = 'heterogenious'
                elif len(protocol_port_list) == 3 :
                    if protocol_port_list[1] == 'ALL' :
                        if len(svc_obj.port) != 1 :
                            object_type = 'heterogenious'
                    elif protocol_port_list[0] == 'ICMP' :
                        if protocol_port_list[1] == 'ECHO' :
                            if len(svc_obj.port) != 1 :
                                object_type = 'heterogenious'
                    else :
                        print "Handle strange proto/dir/port/ notation: %s" % entry
                        object_type = 'special'
                else:
                    # check the homogenity of protocol and direction
                    if seq == 0 :
                        first_protocol = protocol_port_list[0]
                        first_direction = protocol_port_list[1]
                    else :
                        if (first_protocol != protocol_port_list[0]
                            or first_direction != protocol_port_list[1]) :
                            object_type = 'heterogenious'
            if object_type == 'homogenious' and len(svc_obj.port) < 10 :
                # service object
                service_object_list.append(svc_obj)
            elif object_type == 'heterogenious' or len(svc_obj.port) >= 10 :
                # service group
                service_group_object_list.append(svc_obj)
            else :
                if len(svc_obj.port) != 1 :
                    print "Contradiction"
                    sys.exit(1)

        # register all service object
        for svc_obj in service_object_list :
            port_list = []
            direction = 'UNKNOWN'
            for entry in svc_obj.port :
                protocol_port_list = entry.split('/')
                protocol = protocol_port_list[0]
                if len(protocol_port_list) == 2 :
                    # Do not register protocol object : use default one
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 "*", "*", default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif len(protocol_port_list) == 3 :
                    # Do not use 'Protocol name' / ALL object
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 "*", "*", default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif len(protocol_port_list) == 4 :
                    direction = protocol_port_list[1]
                    port = protocol_port_list[2]
                    if port[0] == '=' :
                        if port[1:] == '0' :
                            print "Invalid Port Number: 0 in %s" % svc_obj.name
                        else :
                            port_list.append(port[1:])
                    elif port.count('-') != 0 :
                        port_list.append(port)
                    elif port.startswith("&gt;=") :
                        print "Port Range adjusted: >=%s --> %s-65535" % (port[5:], port[5:])
                        port_list.append("%s-65535" % port[5:])
                    elif port.startswith("&lt;=") :
                        print "Port Range adjusted: <=%s --> 1-%s" % (port[5:], port[5:])
                        port_list.append("1-%s"% port[5:])
                    else :
                        print "Unknown Form %s in %s" % (entry, svc_obj.name)
            if len(port_list) != 0 :
                service_type = self.guess_service_type(svc_obj.name)
                if direction == 'DST' :
                    protocol = protocol_port_list[0]
                    service = MF2_Service_Object(svc_obj.name, protocol, service_type,
                                                 '*', ','.join(port_list), 
                                                 default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif direction == 'SRC' :
                    service = MF2_Service_Object(svc_obj.name, protocol, service_type,
                                                 ','.join(port_list), '*',
                                                 default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                else :
                    print "Unknown Direction %s in %s" % (direction, svc_obj.name)

        # register all service group object
        for svc_grp_obj in service_group_object_list :
            member = []
            for entry in svc_grp_obj.port :
                protocol_port_list = entry.split('/')
                if len(protocol_port_list) == 2 :
                    protocol = protocol_port_list[0]
                    direction = "NONE"
                elif len(protocol_port_list) == 3 :
                    protocol = protocol_port_list[0]
                    direction = "ALL"
                elif len(protocol_port_list) == 4 :
                    protocol = protocol_port_list[0]
                    direction = protocol_port_list[1]
                    port = protocol_port_list[2]
                    if port[0] == '=' :
                        if port[1:] == '0' :
                            print "Invalid Port Number: 0 in %s" % svc_grp_obj.name
                            continue
                        else :
                            port = port[1:]
                    elif port.startswith("&gt;=") :
                        port = "%s-65535" % port[5:]
                    elif port.startswith("&gt;") :
                        port = "%d-65535" % (int(port[5:]) + 1)
                    elif port.startswith("&lt;=") :
                        port = "1-%s" %port[5:]
                    elif port.startswith("&lt;") :
                        port = "1-%d" % (int(port[5:]) - 1)
                else :
                    print "Error: %s" % entry
                    sys.exit(2)
                found = self.mf2_config.lookup_service_object_with_port(protocol, direction, port)
                if found != None :
                    member.append("%s" % found.name)
                else :
                    service_name = "%s_%s" % (protocol, port)
                    service_description = "added as member of %s" % svc_grp_obj.name
                    if direction == 'DST' :
                        service = MF2_Service_Object(service_name, protocol, "NONE",
                                                     "*", port, default_lifetime(protocol),
                                                     service_description)
                    elif direction == 'SRC' :
                        service = MF2_Service_Object(service_name, protocol, "NONE",
                                                     port, "*", default_lifetime(protocol),
                                                     service_description)
                    else :
                        print "Unknown Direction %s in %s" % (direction, svc_grp_obj.name)
                        continue
                    self.mf2_config.add_service(service)
                    member.append("%s" % service_name)

            # try find sub service line by line
            # if not, add new sub service
            service_group = MF2_Service_Group_Object(svc_grp_obj.name, member, svc_grp_obj.description)
            self.mf2_config.add_service_group(service_group)

    def translate_network_list(self, oulim_list, rule) :
        mf2_list = []
        for entry in oulim_list :
            host_obj = self.mf2_config.lookup_host_object(entry)
            if host_obj != None :
                mf2_list.append("%sH %s" % (host_obj.zone[0].upper(), host_obj.name))
                continue
            network_obj = self.mf2_config.lookup_network_object(entry)
            if network_obj != None :
                mf2_list.append("%sN %s" % (netowrk_obj.zone[0].upper(), network_obj.name))
                continue
            group_obj = self.mf2_config.lookup_group_object(entry)
            if group_obj != None :
                mf2_list.append("%sG %s" % (group_obj.zone[0].upper(), group_obj.name))
                continue
            if entry == 'NET-ALL' :
                mf2_list.append("AA ANY")
                continue
            if self.split_object_translate_table.has_key(entry) :
                for sub_entry in self.split_object_translate_table[entry] :
                    group_obj = self.mf2_config.lookup_group_object(sub_entry)
                    if group_obj == None :
                        print "ERROR"
                    mf2_list.append("%sG %s" % (group_obj.zone[0].upper(), group_obj.name))
                continue

            print "Not Found Object : %s from %s" % (entry.encode('utf-8'), rule.sequence)
            
            #sys.exit(3)
        return mf2_list

    def translate_service_list(self, oulim_list, rule) :
        mf2_list = []
        for entry in oulim_list :
             svc_obj = self.mf2_config.lookup_service_object(entry)
             if svc_obj != None :
                 if entry == 'ALL' :
                     print "Found ALL in service list"
                     sys.exit(3)
                 mf2_list.append("SS %s" % svc_obj.name)
                 continue
             svc_grp_obj = self.mf2_config.lookup_service_group_object(entry)
             if svc_grp_obj != None :
                 if entry == 'ALL' :
                     print "Found ALL in service group list"
                     sys.exit(3)
                 mf2_list.append("SG %s" % svc_grp_obj.name)
                 continue
             #print rule
             print ("Not Found: %s in %s" % (entry, rule.sequence)).encode('utf-8')
             #sys.exit(3)
        if len(mf2_list) == 0 :
            mf2_list.append("SA ALL")
        return mf2_list
            
    def translate_fw4_rule(self) :
        for rule in self.rules :
            src_list = self.translate_network_list(rule.source, rule)
            dst_list = self.translate_network_list(rule.destination, rule)
            svc_list = self.translate_service_list(rule.service, rule)
            
            # grouping src and dst
            rule_cnt = 0
            src_group = { }
            dst_group = { }
            for src in src_list :
                if not src_group.has_key(src[0]) :
                    src_group[src[0]] = []
                src_group[src[0]].append(src)
            for dst in dst_list :
                if not dst_group.has_key(dst[0]) :
                    dst_group[dst[0]] = []
                dst_group[dst[0]].append(dst)

            rule_cnt = len(src_group) * len(dst_group)

            for src_zone in src_group :
                for dst_zone in dst_group :
                    if rule_cnt > 1 :
                        description = '%s (%s -> %s)' % (rule.description, src_zone, dst_zone)
                    else :
                        description = rule.description
                    mf2_rule = MF2_FW4_Rule(rule.status == 'Enable', 
                                            src_group[src_zone], 
                                            dst_group[dst_zone],
                                            svc_list, 
                                            rule.action, 
                                            rule.log == 'Enable', 
                                            description)
                    self.mf2_config.add_fw4_rule(mf2_rule)
                    if rule.direction == 'BI' :
                        if rule_cnt > 1 :
                            description = '%s (%s -> %s)' % (rule.description, dst_zone, src_zone)
                        else :
                            description = rule.description
                        mf2_rule = MF2_FW4_Rule(rule.status == 'Enable',
                                                dst_group[dst_zone],
                                                src_group[src_zone],
                                                svc_list,
                                                rule.action,
                                                rule.log == 'Enable',
                                                rule.description)
                        self.mf2_config.add_fw4_rule(mf2_rule)

    def process_route(self, route_list) :
        route_table = []
        route_table_columns = []
        t = route_list[1]
        for col in t :
            if col.__str__().strip() != '':
                route_table_columns.append(col.b.contents[0].strip())
        route = Route()
        route.process_columns(route_table_columns)
        route_table.append(route)        
        for route_entry in route_list[2:] :
            route_table_columns = []
            for col in route_entry :
                if col.__str__().strip() != '':
                    route_table_columns.append(h.unescape(
                            col.contents[0]).strip())
            route = Route()
            route.process_columns(route_table_columns)
            #route.show()
            route_table.append(route)

        route_table.sort(lambda x, y: not cmp(x.netmask, y.netmask))
        self.route_table = route_table

    def process_network(self, network_list) :
        network_columns = []
        for col in network_list[0] :
            if col.__str__().strip() != '':
                t = col.b.contents[0].__str__().strip()
                network_columns.append(t)

        interface_list = []

        interface = Interface()
        interface.set_name(network_columns[0])
        interface.set_addr(network_columns[1])
        interface.set_hw_addr(network_columns[2])
        interface.set_status(network_columns[3])
        interface.set_zone(network_columns[4])
        range_end = None
        #interface.show()
        interface_list.append(interface) # append first column

        for network in network_list[1:range_end] :
            #print network[0]
            network_columns = []
            for col in network :
                if col.__str__().strip() != '':
                    network_columns.append(col)

            interface = Interface()
            process_network_name(interface, network_columns[0])
            process_network_address(interface, network_columns[1])
            process_network_hw_addr(interface, network_columns[2])
            process_network_status(interface, network_columns[3])
            process_network_zone(interface, network_columns[4])
            
            #interface.show()
            interface_list.append(interface)

        self.network_interfaces = interface_list

    def process_network_group(self, ng_list) :
        network_group_columns = []
        # Process header
        for col in ng_list[0] :
            if col.__str__().strip() != '' :
                network_group_columns.append(col.contents[0].strip())

        network_object = NetworkObject()
        network_object.set_name(network_group_columns[0])
        network_object.set_description(network_group_columns[1])
        network_object.set_zone(network_group_columns[2])
        network_object.set_level(network_group_columns[3])
        network_object.set_type(network_group_columns[4])
        network_object.set_addr(network_group_columns[5])
        network_object.set_etc(network_group_columns[6])
        
        network_object_list = []
        network_object_list.append(network_object)
            
        for network_group in ng_list[1:] :
            network_group_columns = []
            for col in network_group :
                if col.__str__().strip() != '' :
                    network_group_columns.append(col)
            network_object = NetworkObject()
            process_network_group_name(network_object,
                                       network_group_columns[0])
            process_network_group_description(network_object,
                                              network_group_columns[1])
            process_network_group_zone(network_object,
                                       network_group_columns[2])
            process_network_group_level(network_object,
                                        network_group_columns[3])
            process_network_group_type(network_object,
                                       network_group_columns[4])
            process_network_group_addr(network_object,
                                       network_group_columns[5])
            process_network_group_etc(network_object,
                                      network_group_columns[6])
            network_object_list.append(network_object)
        self.network_objects = network_object_list

    def process_service(self, service_list, protocol = 'None') :
        service_object_list = self.service_objects
        for service in service_list[1:] :
            col_list = []
            for col in service :
                if col.__str__().strip() != '':
                    col_list.append(col)
            #print len(col_list)
            service = Service(protocol)
            process_service_name(service, col_list[0])
            process_service_description(service, col_list[1])
            process_service_port(service, col_list[2])
            service_object_list.append(service)

    def process_rule(self, rule_list) :
        rule_entry_list = []
        rule = rule_list[0]
        for rule in rule_list[2:] :
            columns = []
            for col in rule :
                if col.__str__().strip() != '':
                    columns.append(col)
            if len(columns) == 15 :
                converted = process_rule_entry(columns)
                rule_entry_list.append(converted)
            elif len(columns) == 1 :
                process_rule_description(converted, columns[0])
        self.rules = rule_entry_list

        # discard two blocks : 
        #	One for <br /> 
        #	Another for 3 buttons : (Add Rule) (Apply) (Current List)

    def process_nat(self, nat_list) :
        #for col in nat_list[0]:
        #    if col.__str__().strip() != '' :
        #        print col.b.font.contents[0].strip().encode('utf-8')
        nat_rule_list = []
        for nat in nat_list[2:] :
            col_list = []
            for col in nat :
                if col.__str__().strip() != '' :
                    col_list.append(col)
            nat_rule = NAT_RULE()
            process_nat_sequence(nat_rule, col_list[0])
            process_nat_type(nat_rule, col_list[1])
            process_nat_nic(nat_rule, col_list[2])
            process_nat_private_addr(nat_rule, col_list[3])
            process_nat_public_addr(nat_rule, col_list[4])
            process_nat_protocol(nat_rule, col_list[5])
            process_nat_port(nat_rule, col_list[6])
            process_nat_status(nat_rule, col_list[7])
            process_nat_etc(nat_rule, col_list[8])
            # process_nat_description(nat_rule, col_list[9])
            nat_rule_list.append(nat_rule)
        self.nat_rules = nat_rule_list

    def show(self) :
        if self.route_table != None and len(self.route_table) != 0 :
            print
            print "Route Table"
            print
            for entry in self.route_table :
                print entry
        if (self.network_interfaces != None 
            and len(self.network_interfaces) != 0) :
            print
            print "Network Interface / Zone"
            print
            for entry in self.network_interfaces :
                print entry
        if (self.network_objects != None 
            and len(self.network_objects) != 0) :
            print
            print "Network Objects"
            print
            for entry in self.network_objects :
                print entry
        if len(self.service_objects) != 0 :
            print
            print "Service Objects"
            print
            for entry in self.service_objects :
                print entry
        if self.rules != None and len(self.rules) != 0 :
            print
            print "Rules"
            print
            for entry in self.rules :
                print entry
        if self.nat_rules != None and len(self.nat_rules) != 0:
            print
            print "NAT Rules"
            print
            for entry in self.nat_rules :
                print entry

class Rule :
    def __init__(self) :
        self.action = ""
        self.direction = ""
        self.sequence = ""
        self.nic = ""
        self.source = ""
        self.destination = ""
        self.service = ""
        self.status = ""
        self.log = ""
        self.description = ""
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
                self.action, self.direction, self.sequence, self.nic,
                ', '.join(self.source), ', '.join(self.destination),
                ', '.join(self.service), self.status, 
                self.log, self.description)).encode('utf-8')
    def show(self) :
        print ("%s:%s:%s:%s:%s:%s:%s:%s" % (
                self.action, self.sequence, self.nic,
                ', '.join(self.source), ', '.join(self.destination), ', '.join(self.service),
                self.status, self.log)).encode('utf-8')
    def set_direction(self, direction) :
        self.direction = direction
    def set_action(self, action) :
        self.action = action
    def set_sequence(self, sequence) :
        self.sequence = sequence
    def set_nic(self, nic) :
        self.nic = nic
    def set_source(self, source) :
        self.source = source
    def set_destination(self, destination) :
        self.destination = destination
    def set_service(self, service) :
        self.service = service
    def set_status(self, status) :
        self.status = status
    def set_log(self, log) :
        self.log = log
    def set_description(self, description) :
        self.description = description

class Interface :
    def __init__(self) :
        self.name = ""
        self.addr = ""
        self.hw_addr = ""
        self.mtu = ""
        self.status = ""
        self.zone = ""
        self.description = ""
        self.level = ""
        self.type = ""
        self.gateway = ""
    def show(self) :
        print "%s:%s:%s:%s:%s" % (
            self.name, self.addr, self.hw_addr, self.status, 
            self.zone).encode('utf-8')
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s" % (
                self.name, self.addr, self.hw_addr, self.status, 
                self.zone)).encoding('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_addr(self, addr) :
        self.addr = addr
    def set_hw_addr(self, hw_addr) :
        self.hw_addr = hw_addr
    def set_mtu(self, mtu) :
        self.mtu = mtu
    def set_status(self, status) :
        self.status = status
    def set_zone(self, zone) :
        self.zone = zone
    def set_description(self, description) :
        self.description = description
    def set_level(self, level) :
        self.level = level
    def set_type(self, type) :
        self.type = type
    def set_gateway(self, gateway) :
        self.gateway = gateway

class NetworkObject :
    def __init__(self) :
        self.name = ""
        self.addr = ""
        self.level = ""
        self.description = ""
        self.type = ""
        self.etc = ""
        self.zone = ""
    def show(self) :
        print "%s:%s:%s:%s:%s:%s" % (
            self.name, self.addr, self.level, self.description, 
            self.type, self.etc)
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s:%s" % (
                self.name, self.addr, self.level, self.zone, self.type,
                self.description)).encode('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_zone(self, zone) :
        self.zone = zone
    def set_addr(self, addr) :
        self.addr = addr
    def set_level(self, level) :
        self.level = level
    def set_description(self, description) :
        self.description = description
    def set_type(self, object_type) :
        self.type = object_type
    def set_etc(self, etc) :
        self.etc = etc

class Service :
    def __init__(self, protocol) :
        self.protocol = protocol
        self.name = ""
        self.port = ""
        self.description = ""
    def show(self) :
        print "%s:%s:%s:%s" % (
            self.name, self.port, self.protocol, self.description)
    def __repr__(self) :
        return ("%s:%s:%s:%s" % (
                self.name, self.port, self.protocol, self.description)).encode('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_port(self, port) :
        self.port = port
    def set_description(self, description) :
        self.description = description

class NAT_RULE :
    def __init__(self) :
        self.description = ""
    def show(self) :
        print "%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
            self.sequence, self.type, self.nic, self.private_addr,
            self.public_addr, self.protocol, self.port,
            self.status, self.description)
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
            self.sequence, self.type, self.nic, self.private_addr,
            self.public_addr, self.protocol, self.port,
            self.status, self.description)).encode('utf-8')
    def set_sequence(self, sequence) :
        self.sequence = sequence
    def set_type(self, type) :
        self.type = type
    def set_nic(self, nic) :
        self.nic = nic
    def set_private_addr(self, private_addr) :
        self.private_addr = private_addr
    def set_public_addr(self, public_addr) :
        self.public_addr = public_addr
    def set_protocol(self, protocol) :
        self.protocol = protocol
    def set_port(self, port) :
        self.port = port
    def set_status(self, status) :
        self.status = status
    def set_description(self, description) :
        self.description = description

class Route :
    def __init__(self) :
        pass
    def process_columns(self, route_table_columns) :
        self.set_address(route_table_columns[0])
        self.set_netmask(route_table_columns[1])
        self.set_gateway(route_table_columns[2])
        self.set_interface(route_table_columns[3])
    def set_address(self, addr) :
        self.address = addr
    def set_netmask(self, netmask) :
        mask = { '255': 8, '0': 0, '128': 1, '192': 2, '224': 3, '240': 4, '248': 5, '252': 6, '254': 7 }
        mask_list = netmask.split('.')
        if netmask == u'넷마스크' :
            self.netmask = -1
            return
        if len(mask_list) != 4 :
            print "Error in route table entry", netmask
            self.netmask = -1
            return
        bit_count = 0
        for node in mask_list :
            bit_count += mask[node]
        self.netmask = bit_count
    def set_gateway(self, gateway) :
        self.gateway = gateway
    def set_interface(self, interface) :
        self.interface = interface
    def show(self) :
        print ("%s/%d -> %s : %s" % (
                self.address, self.netmask, 
                self.gateway, self.interface)).encode('utf-8')
    def __repr__(self) :
        return ("%s/%d -> %s : %s" % (
                self.address, self.netmask, 
                self.gateway, self.interface)).encode('utf-8')
    def is_matched(self, addr) :
        '''
        스트링 주소를 받아서 자신이랑 match되는지 검사
        '''
        def inet_aton(ip_string) :
            num_addr = 0
            #print "ip_string = ", ip_string
            splited_quad = ip_string.split('.')
            if len(splited_quad) == 4 :
                for quad in splited_quad :
                    num_addr <<= 8
                    num_addr += int(quad)
            return num_addr
        def apply_mask(num_addr, bit_count) :
            num_addr >>= (32 - bit_count)
            num_addr <<= (32 - bit_count)
            return num_addr
        t = addr.find('/')
        if t != -1 :
            addr = addr[:t]
        t = addr.find('~')
        if t != -1 :
            addr = addr[:t]
        num_addr = inet_aton(addr)
        num_route = inet_aton(self.address)
        # print "num_addr = %X" % num_addr
        # print "num_addr after apply mask = %X" % apply_mask(num_addr, self.netmask)
        # print "route_addr = %X" % num_route
        # print "route_addr after apply mask = %X" % apply_mask(num_route, self.netmask)
        if apply_mask(num_addr, self.netmask) == apply_mask(num_route, self.netmask) :
            return True
        else :
            return False

class MF2_Config :
    def __init__(self) :
        self.host_objects = []
        self.network_objects = []
        self.group_objects = []
        self.service_objects = []
        self.service_group_objects = []
        self.fw4_rules = []

    # Lookup 함수들

    def lookup_host_object(self, host_obj_name) :
        for host_obj in self.host_objects :
            if host_obj.name == host_obj_name :
                return host_obj
        return None

    def lookup_network_object(self, network_obj_name) :
        for network_obj in self.network_objects :
            if network_obj.name == network_obj_name :
                return network_obj
        return None

    def lookup_group_object(self, group_obj_name) :
        for group_obj in self.group_objects :
            if group_obj.name == group_obj_name :
                return group_obj
        return None

    def lookup_service_object(self, service_obj_name) :
        for service_obj in self.service_objects :
            if service_obj.name == service_obj_name :
                return service_obj
        return None

    def lookup_service_group_object(self, service_group_obj_name) :
        for service_group_obj in self.service_group_objects :
            if service_group_obj.name == service_group_obj_name :
                return service_group_obj
        return None

    def lookup_host_object_with_addr(self, addr) :
        for host_obj in self.host_objects :
            if host_obj.addr == addr :
                return host_obj
        return None

    def lookup_host_object_with_addr_zone(self, addr, zone) :
        for host_obj in self.host_objects :
            if host_obj.addr == addr and host_obj.zone == zone :
                return host_obj
        return None

    def lookup_network_object_with_addr(self, addr) :
        for nw_obj in self.network_objects :
            if nw_obj.addr == addr :
                return nw_obj
        return None

    def lookup_network_object_with_addr_zone(self, addr, zone) :
        for nw_obj in self.network_objects :
            if nw_obj.addr == addr and nw_obj.zone == zone:
                return nw_obj
        return None


    def lookup_service_object_with_port(self, protocol, direction, port) :
        "프로토콜/포트/포트종류(출발지,서비스)를 가지고 서비스 객체를 찾는 함수"
        for service_obj in self.service_objects :
            #print "service_obj.protocol = %s" % service_obj.protocol
            #print "protocol = %s" % protocol
            if service_obj.protocol == protocol :
                if direction == 'SRC' :
                    if service_obj.src_port == port :
                        return service_obj
                elif direction == 'DST' :
                    if service_obj.dst_port == port :
                        return service_obj
                else :
                    # 방향이 없거나 모든 방향에 대한 것인 경우
                    # print "Direction = %s" % direction
                    return service_obj
        return None

    # Add 함수들

    def add_host(self, host_obj) :
        self.host_objects.append(host_obj)

    def add_network(self, nw_obj) :
        self.network_objects.append(nw_obj)
                
    def add_group(self, group_obj) :
        self.group_objects.append(group_obj)
    
    def add_service(self, service_obj) :
        self.service_objects.append(service_obj)

    def add_service_group(self, service_group_obj) :
        self.service_group_objects.append(service_group_obj)

    def add_fw4_rule(self, fw4_rule) :
        self.fw4_rules.append(fw4_rule)

    # Show 함수들
    
    def show_network_objects(self) :
        print "Host Objects"
        for host in self.host_objects :
            print host
        print
        print "Network Objects"
        for network in self.network_objects :
            print network
        print
        print "Group Objects"
        for group in self.group_objects :
            print group
        print

    def show_service_objects(self) :
        print "Service Objects"
        for service in self.service_objects :
            print service
        print
        print "Service Group Objects"
        for service_group in self.service_group_objects :
            print service_group
        print

    def show_fw4_rules(self) :
        print "FW4 Rules"
        for rule in self.fw4_rules :
            print rule
        print

    # MF2 정책 추가
        
    def run_mf2_gui_command(self, cmd) :
        proc = subprocess.Popen([ "cli", "-g", "-c", "conf t", "-c", cmd ],
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out_data, err_data) = proc.communicate()
        if err_data != "" :
            print "Command = ", cmd
            print "Error  = ", err_data
            return '-2', err_data
            #sys.exit(1)

        out_data_list = out_data.split('\n')
        if out_data_list[0] != "OK" :
            code_detail = out_data_list[1].split('^')
            if len(code_detail) < 2 :
                return '-1', out_data
            else:
                return code_detail[0], code_detail[1]
        return '0', "OK"


    # object host NAME zone ZONE ip ADDR description DESCRIPTION
    # object host |^NAME^ZONE^ADDR^DESCRIPTION^F
    def enter_host_object_configs(self) :
        error_messages = []
        print "Start to generate host objects"
        total = len(self.host_objects)
        for seq, host in enumerate(self.host_objects) :
            cmd_list = [ "object", "host", escape_space(host.name),
                         "zone", host.zone,
                         "ip", host.addr,
                         "description", escape_space(host.description) ] 
            cmd = " ".join(cmd_list)
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430065' :
                spec_list = [ '|', escape_space(host.name), host.zone, 
                              host.addr, escape_space(host.description), 'F' ]
                cmd = "object host %s" % "^".join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                error_messages.append("Error <%s> returned from <%s>" % (msg.strip(), cmd))
                #sys.exit(1)
            update_progress("Host Object: ", total, seq)
        print
        print '\n    '.join(error_messages)

    # object network NAME zone ZONE ip (network|range) ADDR/MASK description DESCRIPTION
    def enter_network_object_configs(self) :
        error_messages = []
        print "Start to generate network objects"
        total = len(self.network_objects)
        for seq, network in enumerate(self.network_objects) :
            if network.addr.find('~') == -1 :
                cmd = ("object network %s zone %s ip network %s description %s"
                       % (escape_space(network.name), network.zone,
                          network.addr, 
                          escape_space(network.description)))
            else :
                cmd = ("object network %s zone %s ip range %s description %s"
                       % (escape_space(network.name), network.zone,
                          network.addr.replace("~", " "), 
                          escape_space(network.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430081' or res == '430078' :
                if network.addr.find('~') == -1 :
                    # object network C^|^MMM^internal^10.21.0.0^16^mmm^F
                    base_mask = network.addr.split('/')
                    spec_list = [ 'C', '|', escape_space(network.name), network.zone,
                                  base_mask[0], base_mask[1],
                                  escape_space(network.description), 'F' ]
                else :
                    # object network R^|^TTT^internal^23.43.0.0^23.43.255.255^TTT^F
                    from_to = network.addr.split('~')
                    spec_list = [ 'R', '|', escape_space(network.name), network.zone,
                                  from_to[0], from_to[1],
                                  escape_space(network.description), 'F' ]
                cmd = 'object network %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                error_messages.append("Error <%s> returned from <%s>" % (msg.strip(), cmd))
                #sys.exit(1)
            update_progress("Network Object: ", total, seq)
        print
        print '\n    '.join(error_messages)

    def enter_group_object_configs(self) :
        error_messages = []
        print 'Start to generate group objects'
        total = len(self.group_objects)
        for seq, network_group in enumerate(self.group_objects) :
            temp_file = tempfile.NamedTemporaryFile(prefix = 'group_child_', 
                                                    dir = '/tmp/', delete = False)
            for member in network_group.member :
                #print escape_space(member)
                print >> temp_file, member.encode('utf-8')
            temp_file.close()
            
            cmd = ("object group %s zone %s child %s description %s"
                   % (escape_space(network_group.name), network_group.zone,
                      temp_file.name, escape_space(network_group.description)))

            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430041' :
                # object group |^VVV^internal^/tmp/group_child_3113547543568871.tmp^|^F
                spec_list = [ '|', escape_space(network_group.name), network_group.zone, 
                              temp_file.name, escape_space(network_group.description), 'F' ]
                cmd = "object group %s" % "^".join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                error_messages.append('Error <%s> returned from <%s>' % (msg.strip(), cmd))
                #sys.exit(1)
            os.unlink(temp_file.name)
            update_progress("Group Object: ", total, seq)
        print
        print '\n    '.join(error_messages)

    # object service NAME service-type SVC_TYPE protocol PROTOCOL source-port SRC_PORT \
    #        service-port SVC_PORT lifetime LIFETIME priority PRIORITY sysflag ?? description DESCRIPTION
    def enter_service_object_configs(self) :
        error_messages = []
        print 'Start to generate service objects'
        total = len(self.service_objects)
        for seq, service in enumerate(self.service_objects) :
            cmd = ("object service %s service-type %s protocol %s source-port %s service-port %s lifetime %s priority %d sysflag N description %s"
                   % (escape_space(service.name), service.svc_type, service.protocol, 
                      service.src_port, service.dst_port, service.life_time, 
                      seq, escape_space(service.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430105' and msg.startswith('duplicate contents') :
                # object service |^fff^NONE^TCP^*^114^1800^1^N^|^F
                spec_list = [ '|', escape_space(service.name), service.svc_type,
                              service.protocol, service.src_port, service.dst_port,
                              "%d" % service.life_time, "%d" % seq, "N", 
                              escape_space(service.description), 'F' ]
                cmd = 'object service %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            elif res == '430105' and msg.startswith('duplicate entry') :
                error_messages.append('Warning <%s> returned from <%s>' % (msg.strip(), cmd))
                error_messages.append(" => Duplicated entry for service: Just Skip")
                res = '0'
            elif res == '430105' and msg.startswith('pool full') :
                error_messages.append('Warning <%s> returned from <%s>' % (msg.strip(), cmd))
                error_messages.append( " => Too many ports for service: Just Skip")
                res = '0'
            if res != '0' :
                error_messages.append('Error <%s> returned from <%s>' % (msg.strip(), cmd))
                #sys.exit(1)
            update_progress("Service Object: ", total, seq)
        print
        print '\n    '.join(error_messages)

    def enter_service_group_object_configs(self) :
        error_messages = []
        print 'Start to generate service-group objects'
        total = len(self.service_group_objects)
        for seq, service_group in enumerate(self.service_group_objects) :
            temp_file = tempfile.NamedTemporaryFile(prefix = 'service_group_child_', dir = '/tmp/', delete = False)
            for member in service_group.member  :
                print >> temp_file, "SS %s" % escape_space(member)
            temp_file.close()
            
            cmd = ("object service-group %s child %s description %s"
                   % (escape_space(service_group.name), temp_file.name,
                      escape_space(service_group.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430107' :
                # object sgroup |^bbb^/secui/gui/MF2Tomcat/gui_temp/service_group_child_3165569169784269.tmp^|^F
                spec_list = [ '|', escape_space(service_group.name), temp_file.name,
                              escape_space(service_group.description), 'F' ]
                cmd = 'object sgroup %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                error_messages.append('Error <%s> returned from <%s>' % (msg.strip(), cmd))
                #sys.exit(1)
            os.unlink(temp_file.name)
            update_progress("Service Group Object: ", total, seq)
        print
        print '\n    '.join(error_messages)

    # firewall rule Y^1^^IH|10.240.150.131_I^EN|10.140.39.0-41.255_E^SS|SQLNET_1521^allow^ANY^N|NONE^NONE^NONE^NONE^Y^N^default^NONE
    def enter_fw4_rule_configs(self) :
        error_messages = []
        total = len(self.fw4_rules)
        print 'Start to generate IPv4 firewall rules'
        for seq, rule in enumerate(self.fw4_rules) :
            rule_spec_list = [ "Y" if rule.use else "N", "%d" % (seq + 1), "", 
                               escape_space(",".join(rule.source)), 
                               escape_space(",".join(rule.destination)), 
                               escape_space(",".join(rule.service)),
                               rule.action, "ANY", "N|NONE", "NONE", "NONE", "NONE",
                               "Y" if rule.log else "N", "N", "default", 
                               escape_space(rule.description) ]
            cmd = "firewall rule %s" % "^".join(rule_spec_list)
            res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                error_messages.append('Error <%s> returned from <%s>' % (msg.strip(), cmd))
                #sys.exit(1)
            update_progress("Firewall Rule : ", total, seq)
        print
        print '\n    '.join(error_messages)

    def enter_configs(self) :
        self.run_mf2_gui_command("object cancel")
        self.run_mf2_gui_command('firewall cancel')

        self.enter_host_object_configs()
        self.enter_network_object_configs()
        self.enter_group_object_configs()
        
        self.enter_service_object_configs()
        self.enter_service_group_object_configs()

        #self.run_mf2_gui_command("object apply")

        self.enter_fw4_rule_configs()

    def apply_configs(self) :
        pass

class MF2_Host_Object :
    def __init__(self, name, zone, addr, description) :
        self.name = name
        self.zone = zone
        self.addr = addr
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s" % (self.name, self.zone, self.addr, 
                                   self.description)).encode('utf-8')

class MF2_Network_Object :
    def __init__(self, name, zone, addr, description) :
        self.name = name
        self.zone = zone
        self.addr = addr
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s" % (self.name, self.zone, self.addr, 
                                   self.description)).encode('utf-8')

class MF2_Group_Object :
    def __init__(self, name, zone, member, description) :
        self.name = name
        self.zone = zone
        self.member = member
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%d^%s" % (self.name, self.zone, len(self.member), 
                                   self.description)).encode('utf-8')

class MF2_Service_Object :
    def __init__(self, name, protocol, svc_type, src_port, dst_port, life_time, description) :
        self.name = name
        self.protocol = protocol
        self.svc_type = svc_type
        self.src_port = src_port
        self.dst_port = dst_port
        self.life_time = life_time
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s^%s^%s^%s" % (self.name, self.protocol, self.svc_type,
                                            self.src_port, self.dst_port, self.life_time,
                                            self.description)).encode('utf-8')

class MF2_Service_Group_Object :
    def __init__(self, name, member, description) :
        self.name = name
        self.member = member
        self.description = description

    def __repr__(self) :
        return ("\t%s^%s^%s" % (self.name, 
                                ",".join([ member.name for member in self.member ]), 
                                self.description)).encode('utf-8')

class MF2_FW4_Rule :
    def __init__(self, use, source, destination, service, action, log, description) :
        self.use = use
        self.source = source
        self.destination = destination
        self.service = service
        self.action = action
        self.log = log
        self.description = description

    def __repr__(self) :
        src_list = []
        source = ','.join(self.source)
        destination = ','.join(self.destination)
        service = ','.join(self.service)
        return ("\t%s^%s^%s^%s^%s^%s" % (self.use, source, destination, service,
                                         self.action, self.log)).encode('utf-8')

h = HTMLParser.HTMLParser()

# 
def get_img_src_path_base(from_where) :
    src_attr = from_where.img['src']
    urlparsed_src_attr = urlparse.urlparse(src_attr)
    path_part_src_attr = urlparsed_src_attr.path
    base_part_src_attr = os.path.basename(path_part_src_attr)
    return base_part_src_attr

def strip_nbsp(t) :
    return t.replace('&nbsp;', ' ').strip()

def remove_extra_space(t) :
    return ' '.join(t.split())

# load data into memory
def read_whole_file(file_name) :
    f = open(file_name)
    r = f.read()
    f.close()
    return r

def get_html_part(data) :
    r = []
    i = 0
    msg = email.message_from_string(data)
    for part in msg.walk() :
        if part.get_content_type() == 'application/octet-stream' :
            # dbg_out(">>>>>>> %d" % i)
            i += 1
            r.append(part.get_payload())
    return r

def extract_main_part(config, filename) :
    r = ''
    data = read_whole_file(filename)
    html_docs = get_html_part(data)

    soup = BeautifulSoup(quopri.decodestring(html_docs[2]).decode('cp949'))
        #soup = BeautifulSoup(html_docs[2])
    allTable = soup.findAll("table")
    if filename == 'network.mht' :
        tpos = 1
    elif filename == 'rule.mht' :
        tpos = 2
        index = 0
        for table in allTable :
            if table.__str__().find("javascript") != -1:
                tpos = index + 2
                break
            index = index + 1
    elif filename == 'nat.mht' :
        tpos = 2
    elif filename == 'service.mht' :
        tpos = 1
    elif filename == 'routing.mht' or filename == 'route.mht' :
        tpos = 1
    elif (filename == 'network group.mht' or
          filename == 'network_group.mht') :
        tpos = 1
    else :
        tpos = -6
    for child in allTable[tpos].tbody.table.tbody:
        r += child.__str__()
    r = '<html><body>' + r + '</body></html>'
    newsoup = BeautifulSoup(r)
    child_list = []
    for child in newsoup.body:
        if child.__str__().strip() != '' :
            child_list.append(child)
    return child_list

def extract_main_part_new(filename) :
    r = ''
    data = read_whole_file(filename)
    html_doc = get_html_part(data)
    soup = BeautifulSoup(quopri.decodestring(html_doc))
    for child in soup.table.tbody:
        r += child.__str__()
    r = '<html><body>' + r + '</body></html>'
    newsoup = BeautifulSoup(r)
    child_list = []
    for child in newsoup.body:
        if child.__str__().strip() != '' :
            child_list.append(child)
    return child_list

def process_network_name(interface, name) :
    if name.b != None :
        t = h.unescape(name.b.a.contents[0]).strip()
    else :
        t = h.unescape(name.contents[0]).strip()
    interface.set_name(t)

def process_network_address(interface, address) :
    addr_list = []
    for addr in address :
        t = h.unescape(addr)
        if t.strip != None :
            t = remove_extra_space(t.strip())
            if t != '' :
                if t[0] == '(' : # 괄호로 시작하면 마스크
                    addr_list[-1] += '/' + t[1:-1] # A.B.C.D / AM.BM.CM.DM 형으로 저장
                elif t.startswith("GW ") : # GW로 시작하면 gateway
                    interface.set_gateway(t[3:])
                else :
                    addr_list.append(t) # 주소만 있으면 주소만 저장
    interface.set_addr(addr_list)

def process_network_hw_addr(interface, hw_addr) :
    interface.set_hw_addr(hw_addr.contents[0])

def process_network_mtu(interface, mtu) :
    interface.set_mtu(mtu.contents[0])

def process_network_status(interface, status) :
    if status.font != None :
        t = status.font.contents[0]
    else :
        t = status.contents[0]
    #print t.strip()
    interface.set_status(remove_extra_space(t))

def process_network_zone(interface, zone) :
    #print zone
    zone_string = zone.contents[0]
    if zone_string.startswith(u'외부') :
        interface.set_zone('external')
    elif zone_string.startswith(u'내부') :
        interface.set_zone('internal')
    elif zone_string.startswith(u'서비스') :
        interface.set_zone('dmz')
    else :
        interface.set_zone('unknown')

def process_network_description(interface, description) :
    if description.font != None :
        t = h.unescape(description.font.contents[0]).strip()
    else :
        t = h.unescape(description.contents[0]).strip()
        
    # dbg_out(t)
    interface.set_description(t)

def process_network_level(interface, level) :
    if level.font != None :
        interface.set_level(h.unescape(level.font.contents[0]).strip())
    else :
        interface.set_level(h.unescape(level.contents[0]).strip())

def process_network_type(interface, addr_type) :
    if addr_type.img != None :
        icon = os.path.basename(
            urlparse.urlparse(addr_type.img.attrs[1][1]).path)
        if icon == 'i_host.gif' :
            interface.set_type('Host')
        elif icon == 'i_network.gif' :
            interface.set_type('Network')
        elif icon == 'i_mhost.gif' :
            interface.set_type('Host Group')
        else :
            dbg_out('Invalid Interface Address Type: %s' % icon)

def process_network_group_name(n_object, name) :
    if name.a != None :
        n_object.set_name(escape_special_for_name(name.a.contents[0].strip()))
    else :
        n_object.set_name(escape_special_for_name(strip_nbsp(name.contents[0].strip())))

def process_network_group_description(n_object, description) :
    #if description.contents[0] != '&nbsp;' :
    #    n_object.set_description(h.unescape(description.contents[0]))
    #else :
    #    n_object.set_description('')
    if description.contents != None :
        t = description.contents[0].strip()
        if t != '&nbsp;' :
            descr = strip_nbsp(remove_extra_space(t))
            if descr == '' :
                descr = ' '
            n_object.set_description(descr)

def process_network_group_zone(n_object, zone) :
    icon = get_img_src_path_base(zone)
    if icon == 'i_trust.gif' :
        n_object.set_zone('internal')
    elif icon == 'i_untrust.gif' :
        n_object.set_zone('external')
    elif icon == 'i_service.gif' :
        n_object.set_zone('dmz')
    else :
        print "Unknown Network Group Zone : %s in %s" % (icon, n_object.name)

def process_network_group_level(n_object, level) :
    if level.contents[0] != '&nbsp;' :
        n_object.set_level(strip_nbsp(level.contents[0].strip()))
    else :
        n_object.set_level('')

def process_network_group_type(n_object, type) :
    if type.img != None :
        icon = get_img_src_path_base(type)
        if icon == 'i_host.gif' :
            n_object.set_type('Host')
        elif icon == 'i_mhost.gif' :
            n_object.set_type('Host Group')
        elif icon == 'i_network.gif' :
            n_object.set_type('Network')
        else :
            dbg_out('Invalid Network Group Type: %s' % icon)
    else :
        n_object.set_type('None')

def process_network_group_addr(n_object, addr) :
    addr_list = []
    for content in addr.contents :
        t = content.__str__()
        if t != '<br />' and t != '&nbsp;' :
            addr_list.append(strip_nbsp(t))
    n_object.set_addr(addr_list)

def process_network_group_etc(n_object, etc) :
    # ignore etc
    pass

def process_service_name(service, name) :
    if name.a != None :
        t = name.a.contents[0].strip()
    else :
        t = name.contents[0].strip()
    
    if t != '&nbsp;' :
        service.set_name(escape_special_for_name(strip_nbsp(remove_extra_space(t))))

def process_service_port(service, port) :
    #print "service_port = %s" % port
    port_list = []
    for content in port.contents :
        t = content.__str__()
        if t != '<br />' and t != '&nbsp;' :
            port_list.append(strip_nbsp(t))
    #print "port_list = %s" % port_list
    service.set_port(port_list)

def process_service_description(service, description) :
    if description.contents != None :
        t = description.contents[0].strip()
        if t != '&nbsp;' :
            descr = strip_nbsp(remove_extra_space(t))
            if descr == '' :
                descr = ' '
            service.set_description(descr)

def process_rule_sequence(rule, sequence) :
    icon = get_img_src_path_base(sequence)
    if icon == 'i_direction.gif' :
        rule.set_direction("UNI")
    elif icon == 'i_bidirection.gif' :
        rule.set_direction("BI")
    else :
        dbg_out('Invalid Rule Direction Icon: %s' % icon)
    t = sequence.a.nextSibling.nextSibling.nextSibling.contents[0].encode('utf-8')
    rule.set_sequence(t)

def process_rule_NIC(rule, nic) :
    t = nic.contents[0].strip()
    rule.set_nic(t)

def process_rule_source(rule, source) :
    t = [ escape_special_for_name(b.contents[0].strip()) for b in source.findAll('b') ]
    rule.set_source(t)

def process_rule_destination(rule, destination) :
    t = [ escape_special_for_name(b.contents[0].strip()) for b in destination.findAll('b') ]
    rule.set_destination(t)

def process_rule_service(rule, service) :
    service_list = [ escape_special_for_name(remove_extra_space(b.contents[0].strip()))
                     for b in service.findAll('b') ]
    rule.set_service(service_list)

def process_rule_user(rule, user) :
    # ignore user in rule
    pass

def process_rule_timerange(rule, timerange) :
    # ignore time range in rule
    pass

def process_rule_etc(rule, etc) :
    # Only handle log status
    icon = get_img_src_path_base(etc)
    if icon == 'logok.gif' :
        rule.set_log('Enable')
    else :
        rule.set_log('Disable')

def process_rule_description(rule, description) :
    # ignore description in rule
    t = remove_extra_space(description.contents[-1])
    rule.set_description(t)

def process_rule_action(rule, action) :
    icon = get_img_src_path_base(action)
    if icon == 'i_qos-allow-4.gif' :
        t = "allow"
    elif icon == 'i_qos-deny.gif':
        t = "deny"
    elif icon == 'i_qos-crypt-4.gif' :
        t = "VPN"
    else :
        dbg_out('Unknown icon for rule action : %s' % icon)
    rule.set_action(t)

def process_rule_log(rule, log) :
    icon = get_img_src_path_base(log)
    # dbg_out(icon)
    if icon == 'i_log.gif' :
        rule.set_log('Enable')
    else :
        rule.set_log('Disable')

def process_rule_status(rule, status) :
    icon = get_img_src_path_base(status)
    if icon == 'i_on.gif' :
        rule.set_status('Enable')
    elif icon == 'i_off.gif' :
        rule.set_status('Disable')
    else :
        dbg_out("Invalid Rule Status : %s" % icon)

def process_rule_entry(rule_columns) :
    rule = Rule()
    process_rule_sequence(rule, rule_columns[0])
    process_rule_NIC(rule, rule_columns[1])
    process_rule_source(rule, rule_columns[2])
    process_rule_destination(rule, rule_columns[3])
    process_rule_service(rule, rule_columns[4])
    process_rule_user(rule, rule_columns[5])
    process_rule_timerange(rule, rule_columns[6])
    process_rule_action(rule, rule_columns[7])
    process_rule_log(rule, rule_columns[11])
    process_rule_status(rule, rule_columns[13])
    return rule

def process_nat_sequence(nat_rule, sequence) :
    #print sequence.contents[0]
    t = sequence.contents[0]
    nat_rule.set_sequence(t)
    pass

def process_nat_type(nat_rule, type) :
    t = get_img_src_path_base(type)
    nat_rule.set_type(t)
    pass

def process_nat_nic(nat_rule, nic) :
    nat_rule.set_nic(strip_nbsp(nic.contents[0].strip()))
    pass

def process_nat_private_addr(nat_rule, private_addr) :
    t = private_addr.b.contents[0].strip()
    nat_rule.set_private_addr(t)
    pass

def process_nat_public_addr(nat_rule, public_addr) :
    t = public_addr.b.contents[0].strip()
    nat_rule.set_public_addr(t)
    pass

def process_nat_protocol(nat_rule, protocol) :
    #print protocol.contents[0]
    nat_rule.set_protocol(protocol.contents[0])
    pass

def process_nat_port(nat_rule, port) :
    #print port.contents[0]
    nat_rule.set_port(port.contents[0])
    pass

def process_nat_status(nat_rule, status) :
    t = os.path.basename(
        urlparse.urlparse(status.img.attrs[0][1]).path)
    nat_rule.set_status(t)
    pass

def process_nat_etc(nat_rule, etc) :
    # ignore
    # print etc 
    pass

def process_nat_description(nat_rule, description) :
    print description.contents
    nat_rule.set_description(description.contents[0])

def make_oulim_config() :
    # gather information from Oulim's configuration file
    config = Config()

    mht_list = [ fn for fn in os.listdir('.') if fnmatch.fnmatch(fn, "*.mht") ]
    for filename in os.listdir('.') :
        if not fnmatch.fnmatch(filename, "*.mht") :
            continue
        dbg_out("Processing %s" % filename)
        if filename == 'routing.mht' or filename == 'route.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_route(soup)
        elif filename == 'network.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_network(soup)
        elif (filename == 'network group.mht' 
              or filename == 'network_group.mht') :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_network_group(soup)
        elif filename == 'servicetcp.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup, 'TCP')
        elif filename == 'serviceudp.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup, 'UDP')
        elif filename == 'service.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup)
        elif filename == 'rule.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_rule(soup)
        elif filename == 'nat.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_nat(soup)
        else :
            dbg_out('Unhandle config %s ' % filename)
    #config.show()
    return config

if __name__ == '__main__' :
    # 파라미터를 파싱
    parser = optparse.OptionParser(
        description = 'Convert Oulim\'s config to Secui MF2\'s config.')
    #parser.add_option('-i', '--interact', action = 'store_true', 
    #                  help = 'interactive mode')
    parser.add_option('-c', '--clear', action = 'store_true',
                      help = 'clear the cached oulim config')
    (options, args) = parser.parse_args()

    # 매번 파싱하면 너무 시간이 많이 걸려서 pickle에 저장
    if not options.clear and os.access('Oulim.pickle', os.F_OK) :
        with file('Oulim.pickle') as pkl_file:
            config = pickle.load(pkl_file)
    else:
        config = make_oulim_config()
        with file('Oulim.pickle', 'w') as pkl_file:
            pickle.dump(config, pkl_file)

    # 확인
    config.verify_rule()

    # 어울림 설정을 MF2 설정으로 변환
    mf2_config = MF2_Config()
    config.register_mf2_config(mf2_config)
    #config.set_output_file('gen_mf2_rule.sh')
    config.translate_network_object()
    #mf2_config.show_network_objects()
    config.translate_service_object()
    #mf2_config.show_service_objects()
    config.translate_fw4_rule()
    #mf2_config.show_fw4_rules()
    #config.close_output_file()
    mf2_config.enter_configs()
    mf2_config.apply_configs()

    #if options.interact :
    #    print "Start a cmd processor."
    #    cmd_processor = cmd.Cmd()
    #    cmd_processor.cmdloop()
